iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 5
0

前言

今天內容的commit
大多細節沒有辨法一一說明XD
完整的檔案跟程式都在上面

專案結構

Golang寫起來其實很自由,專案結構要很平只有一層也可以,要多層巢狀的也可以,根據你的專案大小來決定,如果功能少少的也不太會擴張,就不要設計成太多層,只會搞到自己。
下面這個模式是我個人用得滿習慣,做後端的API服務應該都適用

config

讀取設定檔

handler

相關API主要邏輯實作的地方

model

全部有關資料庫的操作都放在這邊

pkg

共用的一些func

middleware

中間層,例如登入狀態的驗證

專案比較大的話,我會多拆一個文件夾來放商業邏輯,handler就只做流程的控制
如果這樣還不夠放,會建議拆成一個以上的服務,服務之間用API來溝通。用微服務的架構來避免單一個專案太過巨大,導致以後難以維護。

來寫一個ping pong吧

這邊用到一個叫gin的套件
主要是用來處理一些HTTP的東西,例如router、cookie什麼的,有興趣可以看一下他們的文件

為了系統輸出的格式保持一致,我寫了一些func
request的成功失敗都寫在result這個key
失敗會有相關的error code
pkg/res/res.go

// Success Success
func Success(c *gin.Context, payload interface{}) {
	c.JSON(http.StatusOK, gin.H{
		"result": true,
		"data":   payload,
	})
}

// SystemError SystemError
func SystemError(c *gin.Context, errorCode int, payload interface{}) {
	c.JSON(http.StatusInternalServerError, gin.H{
		"result":     false,
		"error_code": errorCode,
		"msg":        payload,
	})
}

viper用來處理config的套件
簡單來說,它會把config.yaml的值會mapping到Config的struct裡面
config/config.go

// Config Config
type Config struct {
	Mode string `mapstructure:"MODE"`
	Port string `mapstructure:"PORT"`
}

// Init Init
func Init() {
	viper.SetConfigName("config.yaml")
	viper.SetConfigType("yaml")
	viper.AddConfigPath(".")
	if err := viper.ReadInConfig(); err != nil {
		panic(fmt.Errorf("Fatal error config file: %v ", err))
	}
	if err := viper.Unmarshal(&Val); err != nil {
		panic(fmt.Errorf("unable to decode into struct, %v", err))
	}

	log.WithFields(log.Fields{
		"val": Val,
	}).Info("config loaded")
}

config.yaml

MODE: "debug"
Port: 9090

為了方法開發,初期會有一些cross domain的問題,這邊有一個middleware 去處理
middleware/cross.go

func CROSS() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
		c.Writer.Header().Set("Access-Control-Allow-Origin", c.Request.Header.Get("origin"))
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, Token, Set-Cookie, Cookie")
		c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
		c.Writer.Header().Set("Access-Control-Allow-Max-Age", "1728000")

		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(204)
			return
		}

		c.Next()
	}
}

main.go

func main() {
	load()

	gin.SetMode(config.Val.Mode)
	r := gin.Default() // 預設會幫你掛上兩個middleware 1.access log 2.panic自動recovery
	r.Use(middleware.CROSS())

	r.GET("/ping", handler.Ping)

	r.Run(":" + config.Val.Port)

	log.Infof("serve port: %v \n", config.Val.Port)
}

func load() {
	config.Init()
}

來啟動吧~

go run .

https://ithelp.ithome.com.tw/upload/images/20200912/20129767scsLohM8O7.png

本機測試一下API
port是9090,docker-compose那邊有設定
https://ithelp.ithome.com.tw/upload/images/20200912/20129767WjmLj12L6d.png

看起來沒問題
先到這裡好累哦

明天會來講一下Googld OAuth的東西

謝謝大家!!


上一篇
Day4 來個Hello World
下一篇
Day 6 Google OAuth2
系列文
Golang & Vue.js 30天從0打造服務30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言